package com.gitee.wsl.mathematics.geometry.d2.line.ext

import com.gitee.wsl.mathematics.coordinate.d2.Coordinate2
import com.gitee.wsl.mathematics.coordinate.d2.Point2
import com.gitee.wsl.mathematics.geometry.d2.line.LineShape2d
import com.gitee.wsl.mathematics.number.compareTo
import com.gitee.wsl.mathematics.vector.vec2.Vector2

inline fun<T:Number> LineShape2d<T, *>.getIntersectXY(Ax: T, Ay: T, Bx: T, By: T, Cx: T, Cy: T, Dx: T, Dy: T): Point2<T,*>? {
    val a1 = By - Ay
    val b1 = Ax - Bx
    val c1 = a1 * (Ax) + b1 * (Ay)
    val a2 = Dy - Cy
    val b2 = Cx - Dx
    val c2 = a2 * (Cx) + b2 * (Cy)
    val determinant = a1 * b2 - a2 * b1
    if (determinant.isApproxZero()) return null
    val x = (b2 * c1 - b1 * c2) / determinant
    val y = (a1 * c2 - a2 * c1) / determinant
    //if (!x.isFinite() || !y.isFinite()) TODO()
    return createPoint2(x, y) as Point2<T,*>
}

fun<T:Number> LineShape2d<T, *>.getIntersectXY(a: Point2<T,*>, b: Point2<T,*>, c: Point2<T,*>, d: Point2<T,*>): Point2<T,*>? =
    getIntersectXY(a.x, a.y, b.x, b.y, c.x, c.y, d.x, d.y)

fun<T:Number> LineShape2d<T, *>.getLineIntersectionPoint(line: LineShape2d<T, *>): Point2<T,*>? =
    getIntersectXY(x0, y0, x1, y1, line.x0, line.y0, line.x1, line.y1)

fun<T:Number> LineShape2d<T, *>.getIntersectionPoint(line: LineShape2d<T, *>): Point2<T,*>? = getSegmentIntersectionPoint(line)
fun<T:Number> LineShape2d<T, *>.getSegmentIntersectionPoint(line: LineShape2d<T, *>): Point2<T,*>? {
    val out = getIntersectXY(x0, y0, x1, y1, line.x0, line.y0, line.x1, line.y1)
    if (out != null && this.containsPoint(out) && line.containsPoint(out)) return out
    return null
}

fun<T:Number> LineShape2d<T, *>.intersectsLine(line: LineShape2d<T, *>): Boolean = getLineIntersectionPoint(line) != null
fun<T:Number> LineShape2d<T, *>.intersects(line: LineShape2d<T, *>): Boolean = intersectsSegment(line)
fun<T:Number> LineShape2d<T, *>.intersectsSegment(line: LineShape2d<T, *>): Boolean = getSegmentIntersectionPoint(line) != null


/**
 * Finds the intersection point between two [LineSegment]s.
 *
 * Returns [Vector2.INFINITY] if none exists.
 *
 * @param a The first line segment.
 * @param b The second line segment.
 * @param eps How far outside the [t](https://pomax.github.io/bezierinfo/#explanation) value are intersections considered.
 */

fun<T:Number,V:Coordinate2<T,V>> intersection(a: LineShape2d<T, V>, b: LineShape2d<T, V>, eps: Double = 0.0) =
    intersection(a.start, a.end, b.start, b.end, eps)


/**
 * Finds the intersection point between two [LineSegment]s.
 *
 * Returns [Vector2.INFINITY] if none exists.
 *
 * @param a0 The start of the first line segment.
 * @param a1 The end of the first line segment.
 * @param b0 The start of the second line segment.
 * @param b1 The end of the second line segment.
 * @param eps How far outside the [t](https://pomax.github.io/bezierinfo/#explanation) value are intersections considered.
 */
fun<T:Number,V:Coordinate2<T,V>> intersection(a0: V, a1: V, b0: V, b1: V, eps: Double = 0.0): V {
    val x0 = a0.x
    val x1 = a1.x
    val x2 = b0.x
    val x3 = b1.x

    val y0 = a0.y
    val y1 = a1.y
    val y2 = b0.y
    val y3 = b1.y

    a0.run {
        val den = (x0 - x1) * (y2 - y3) - (y0 - y1) * (x2 - x3)

        return if (! abs(den).isApproxZero() ) {
            val px = ((x0 * y1 - y0 * x1) * (x2 - x3) - (x0 - x1) * (x2 * y3 - y2 * x3)) / den
            val py = ((x0 * y1 - y0 * x1) * (y2 - y3) - (y0 - y1) * (x2 * y3 - y2 * x3)) / den

            val s = (-(y1 - y0) * (x0 - x2) + (x1 - x0) * (y0 - y2)) / den
            val t = ((x3 - x2) * (y0 - y2) - (y3 - y2) * (x0 - x2)) / den

            if (t >= 0 - eps && t <= 1 + eps && s >= 0 - eps && s <= 1 + eps) {
                create(px, py)
            } else {
                create(POSITIVE_INFINITY,POSITIVE_INFINITY)
                //Vector2.INFINITY
            }
        } else {
            create(POSITIVE_INFINITY,POSITIVE_INFINITY)
            //Vector2.INFINITY
        }
    }
}